﻿using System;
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Configuration.Repository;
using Ocelot.Middleware;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Ocelot.Provider.Nacos.NacosClient.V2;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Options;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Creator;
using Ocelot.Responses;
using System.Linq;

namespace Ocelot.Provider.Nacos
{
    public class NacosMiddlewareConfigurationProvider
    {
        public static OcelotMiddlewareConfigurationDelegate Get { get; } = GetAsync;

        private static async Task GetAsync(IApplicationBuilder builder)
        {
            var fileConfigRepo = builder.ApplicationServices.GetService<IFileConfigurationRepository>();
            var fileConfig = builder.ApplicationServices.GetService<IOptionsMonitor<FileConfiguration>>();

            var internalConfigCreator = builder.ApplicationServices.GetService<IInternalConfigurationCreator>();
            var internalConfigRepo = builder.ApplicationServices.GetService<IInternalConfigurationRepository>();

            if (UsingNacos(fileConfigRepo))
            {
                await SetFileConfigInNacos(builder, fileConfigRepo, fileConfig, internalConfigCreator, internalConfigRepo);
            }

            var hostLifetime = builder.ApplicationServices.GetService<IHostApplicationLifetime>();
            await builder.UseNacosAspNet(hostLifetime);



            //var internalConfigRepo = builder.ApplicationServices.GetService<IInternalConfigurationRepository>();
            //var config = internalConfigRepo.Get();

            //var hostLifetime = builder.ApplicationServices.GetService<IHostApplicationLifetime>();

            //if (UsingNacosServiceDiscoveryProvider(config.Data))
            //{
            //    await builder.UseNacosAspNet(hostLifetime);
            //}
        }

        private static bool UsingNacos(IFileConfigurationRepository fileConfigRepo)
     => fileConfigRepo.GetType() == typeof(NacosFileConfigurationRepository);


        private static async Task SetFileConfigInNacos(IApplicationBuilder builder,
        IFileConfigurationRepository fileConfigRepo, IOptionsMonitor<FileConfiguration> fileConfig,
        IInternalConfigurationCreator internalConfigCreator, IInternalConfigurationRepository internalConfigRepo)
        {
            // Get the config from Nacos
            var fileConfigFromNacos = await fileConfigRepo.Get();
            if (IsError(fileConfigFromNacos))
            {
                ThrowToStopOcelotStarting(fileConfigFromNacos);
            }
            else if (ConfigNotStoredInNacos(fileConfigFromNacos))
            {
                // there was no config in Consul set the file in config in Nacos
                await fileConfigRepo.Set(fileConfig.CurrentValue);
            }
            else
            {
                // Create the internal config from Nacos data
                var internalConfig = await internalConfigCreator.Create(fileConfigFromNacos.Data);
                if (IsError(internalConfig))
                {
                    ThrowToStopOcelotStarting(internalConfig);
                }
                else
                {
                    // add the internal config to the internal repo
                    var response = internalConfigRepo.AddOrReplace(internalConfig.Data);
                    if (IsError(response))
                    {
                        ThrowToStopOcelotStarting(response);
                    }
                }

                if (IsError(internalConfig))
                {
                    ThrowToStopOcelotStarting(internalConfig);
                }
            }
        }

        private static bool IsError(Response response)
     => response == null || response.IsError;

        private static bool ConfigNotStoredInNacos(Response<FileConfiguration> fileConfigFromConsul)
            => fileConfigFromConsul.Data == null;

        private static void ThrowToStopOcelotStarting(Response config)
    => throw new Exception($"Unable to start Ocelot, errors are: {string.Join(',', config.Errors.Select(x => x.ToString()))}");


        private static bool UsingNacosServiceDiscoveryProvider(IInternalConfiguration configuration)
        {
            return configuration?.ServiceProviderConfiguration != null && configuration.ServiceProviderConfiguration.Type?.ToLower() == "nacos";
        }
    }
}
